home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 December: Technology Seed / ATS Dec. '97.toast / Navigation Services SDK 1.0b1 / Examples / Sampler / Sampler ƒ / drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-11-20  |  22.8 KB  |  822 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        drag.c
  3.  
  4.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  5.  
  6. */
  7.  
  8. #pragma segment DocSeg
  9.  
  10. #ifndef __FOLDERS__
  11. #include <Folders.h>
  12. #endif
  13.  
  14. #ifndef __DRAG__
  15. #include <Drag.h>
  16. #endif
  17.  
  18. #ifndef __WINDOWS__
  19. #include <Windows.h>
  20. #endif
  21.  
  22. #ifndef Common_Defs
  23. #include "Common.h"
  24. #endif
  25.  
  26. static long        caretTime;
  27. static short    caretOffset, caretShow, lastOffset, insertPosition, canAcceptItems;
  28. static short    cursorInContent;
  29.  
  30. #define    gCaretTime    ((short)*((long*)0x02F4))    // provides access to TextEdit's caretTime.
  31.  
  32. short HitTest(Point theLoc, Document** theDoc);
  33. void DrawCaret(short offset, TEHandle theTE);
  34. char GetCharAtOffset(short offset, TEHandle theTE);
  35. Boolean WhiteSpace(char theChar);
  36. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE);
  37. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE);
  38. short GetSelectionSize(Document* theDocument);
  39. Ptr GetSelectedTextPtr(Document* theDocument);
  40.  
  41. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag);
  42. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point showOrigin, RgnHandle hideRgn, Point hideOrigin, void* dragDrawingRefCon, DragReference theDragRef);
  43. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag);
  44. pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow, void* handlerRefCon, DragReference theDrag);
  45.  
  46. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation);
  47.  
  48. extern DragSendDataUPP sendHandler;
  49.  
  50.  
  51. // *****************************************************************************
  52. // *
  53. // *    MyDrawingProc()
  54. // *
  55. // *****************************************************************************
  56. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point /*showOrigin*/, RgnHandle hideRgn, Point /*hideOrigin*/, void* /*dragDrawingRefCon*/, DragReference /*theDragRef*/)
  57. {    
  58.     OSErr        result = paramErr;
  59.     RgnHandle    tempRgn;
  60.  
  61.     switch(message)
  62.         {
  63.         case dragRegionBegin:
  64.             result = noErr;
  65.             break;
  66.  
  67.         case dragRegionDraw:
  68.             XorRgn(showRgn,hideRgn,tempRgn = NewRgn());
  69.             InvertRgn(tempRgn);
  70.             DisposeRgn(tempRgn);
  71.             result = noErr;
  72.             break;
  73.  
  74.         case dragRegionHide:
  75.             InvertRgn(hideRgn);
  76.             result = noErr;
  77.             break;
  78.         }
  79.     return result;
  80. }
  81.  
  82.  
  83. // *****************************************************************************
  84. // *
  85. // *    HitTest()
  86. // *
  87. // *    Given a point in global coordinates, HitTest returns a pointer to a
  88. // *    document structure if the point is inside a document window on the screen.
  89. // *    If the point is not inside a document window, HitTest return NULL in
  90. // *    theDoc. If the point is in a doument window and also in the viewRect of
  91. // *    the document's TextEdit field, HitTest also returns the offset into
  92. // *    the text that corresponds to that point. If the point is not in the text,
  93. // *    HitTest returns -1.
  94. // *
  95. // *****************************************************************************
  96. short HitTest(Point theLoc, Document** theDoc)
  97. {    
  98.     WindowPtr    theWindow;
  99.     short        offset;
  100.  
  101.     *theDoc = 0L;
  102.     offset = -1;
  103.  
  104.     if (FindWindow(theLoc,&theWindow) == inContent)
  105.         {
  106.         if (*theDoc = IsDocumentWindow(theWindow))
  107.             {
  108.             SetPort(theWindow);
  109.             GlobalToLocal(&theLoc);
  110.  
  111.             if ((PtInRect(theLoc, &(**((**theDoc).theTE)).viewRect)) && 
  112.                 (PtInRect(theLoc, &(**((**theDoc).theTE)).destRect)))
  113.                 {
  114.                 offset = TEGetOffset(theLoc,(**theDoc).theTE);
  115.  
  116.                 if ((TEIsFrontOfLine(offset,(**theDoc).theTE)) && (offset) &&            
  117.                         ((*((**((**theDoc).theTE)).hText))[offset - 1] != 0x0D) &&
  118.                         (TEGetPoint(offset - 1,(**theDoc).theTE).h < theLoc.h))
  119.                         {
  120.                         offset--;
  121.                         }
  122.                 }
  123.             }
  124.         }
  125.     return offset;
  126. }
  127.  
  128.  
  129. // *****************************************************************************
  130. // *
  131. // *    DrawCaret()
  132. // *
  133. // *    Draws a caret in a TextEdit field at the given offset. DrawCaret
  134. // *    expects the port to be set to the port that the TextEdit field is in.
  135. // *    DrawCaret inverts the image of the caret onto the screen.
  136. // *
  137. // *****************************************************************************
  138. void DrawCaret(short offset, TEHandle theTE)
  139. {    
  140.     Point    theLoc;
  141.     short    theLine, lineHeight;
  142.  
  143.     // get the coordinates and the line of the offset to draw the caret
  144.     theLoc  = TEGetPoint(offset,theTE);
  145.     theLine = TEGetLine(offset,theTE);
  146.  
  147.     //    For some reason, TextEdit dosen't return the proper coordinates
  148.     //    of the last offset in the field if the last character in the record
  149.     //    is a carriage return. TEGetPoint returns a point that is one line
  150.     //    higher than expected. The following code fixes this problem.
  151.     if ((offset == (**theTE).teLength) && (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D)
  152.         theLoc.v += TEGetHeight(theLine,theLine,theTE);
  153.  
  154.     PenMode(patXor);                                    //    invert the caret when drawing
  155.     lineHeight = TEGetHeight(theLine,theLine,theTE);    // get the height of the line that the offset points to
  156.  
  157.     MoveTo(theLoc.h - 1,theLoc.v - 1);
  158.     Line(0,1 - lineHeight);                                // draw the appropriate caret image
  159.  
  160.     PenNormal();
  161. }
  162.  
  163.  
  164. // *****************************************************************************
  165. // *
  166. // *    GetCharAtOffset()
  167. // *
  168. // *****************************************************************************
  169. char GetCharAtOffset(short offset, TEHandle theTE)
  170. {
  171.     if (offset < 0)
  172.         return(0x0D);
  173.     return(((char *) *((**theTE).hText))[offset]);
  174. }
  175.  
  176.  
  177. // *****************************************************************************
  178. // *
  179. // *    WhiteSpace()
  180. // *
  181. // *****************************************************************************
  182. Boolean WhiteSpace(char theChar)
  183. {
  184.     return((theChar == ' ') || (theChar == 0x0D));
  185. }
  186.  
  187.  
  188. // *****************************************************************************
  189. // *
  190. // *    WhiteSpaceAtOffset()
  191. // *
  192. // *****************************************************************************
  193. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  194. {    
  195.     char theChar;
  196.  
  197.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  198.         return(true);
  199.  
  200.     theChar = ((char *) *((**theTE).hText))[offset];
  201.     return((theChar == ' ') || (theChar == 0x0D));
  202. }
  203.  
  204.  
  205. // *****************************************************************************
  206. // *
  207. // *    InsertTextAtOffset()
  208. // *
  209. // *****************************************************************************
  210. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  211. {
  212.     if (size == 0)
  213.         return;
  214.  
  215.     //    If inserting at the end of a word and the selection does not begin with
  216.     //    a space, insert a space before the insertion.
  217.     if (!WhiteSpaceAtOffset(offset - 1,theTE) && WhiteSpaceAtOffset(offset,theTE) && !WhiteSpace(theBuf[0]))
  218.         {
  219.         TESetSelect(offset,offset,theTE);
  220.         TEKey(' ',theTE);
  221.         offset++;
  222.         }
  223.  
  224.     //    If inserting at the beginning of a word and the selection does not end
  225.     //    with a space, insert a space after the insertion.
  226.     if (WhiteSpaceAtOffset(offset - 1, theTE) && !WhiteSpaceAtOffset(offset, theTE) && !WhiteSpace(theBuf[size - 1]))
  227.         {
  228.         TESetSelect(offset, offset,theTE);
  229.         TEKey(' ',theTE);
  230.         }
  231.  
  232.     TESetSelect(offset,offset,theTE);
  233.     TEStyleInsert(theBuf,size,theStyl,theTE);
  234.     TESetSelect(offset,offset + size,theTE);
  235. }
  236.  
  237.  
  238. // *****************************************************************************
  239. // *
  240. // *    GetSelectionSize()
  241. // *
  242. // *****************************************************************************
  243. short GetSelectionSize(Document* theDocument)
  244. {
  245.     return((**(theDocument->theTE)).selEnd - (**(theDocument->theTE)).selStart);
  246. }
  247.  
  248.  
  249. // *****************************************************************************
  250. // *
  251. // *    GetSelectedTextPtr()
  252. // *
  253. // *****************************************************************************
  254. Ptr GetSelectedTextPtr(Document* theDocument)
  255. {
  256.     return((*(**(theDocument->theTE)).hText) + (**(theDocument->theTE)).selStart);
  257. }
  258.  
  259.  
  260. // *****************************************************************************
  261. // *
  262. // *    MySendDataProc()
  263. // *
  264. // *    Will provide 'styl' data for the drag when requested.
  265. // *
  266. // *****************************************************************************
  267. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag)
  268. {    
  269.     Document*        theDocument = (Document*)refCon;
  270.     StScrpHandle    theStyl;
  271.  
  272.     if (theType == 'styl') 
  273.         {
  274.         theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  275.  
  276.         // Call SetDragItemFlavorData to provide the requested data.
  277.         HLock((Handle) theStyl);
  278.         SetDragItemFlavorData(theDrag,theItem,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0L);
  279.         HUnlock((Handle)theStyl);
  280.         DisposeHandle((Handle)theStyl);
  281.         } 
  282.     else
  283.         return badDragFlavorErr;
  284.  
  285.     return noErr;
  286. }
  287.  
  288.  
  289. // *****************************************************************************
  290. // *
  291. // *    MyReceiveDropHandler()
  292. // *
  293. // *****************************************************************************
  294. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag)
  295. {    
  296.     OSErr                result;
  297.     TEHandle            tempTE;
  298.     Rect                theRect, srcRect;
  299.     unsigned short        items, index;
  300.     ItemReference        theItem;
  301.     DragAttributes        attributes;
  302.     Ptr                    textData;
  303.     StScrpHandle        stylHandle;
  304.     Size                textSize, stylSize;
  305.     short                offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
  306.     Document*            theDocument = (Document*)handlerRefCon;
  307.     WindowOffscreen*    theOffscreen;
  308.     Point                thePoint;
  309.  
  310.     if ((!canAcceptItems) || (insertPosition == -1))
  311.         return(dragNotAcceptedErr);
  312.  
  313.     SetPort(theWindow);
  314.  
  315.     GetDragAttributes(theDrag, &attributes);
  316.     GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  317.  
  318.     moveText = (attributes & dragInsideSenderWindow) &&
  319.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  320.  
  321.     //    Loop through all of the drag items contained in this drag and collect the text
  322.     //    into the tempTE record.
  323.     
  324.     SetRect(&theRect,0,0,0,0);
  325.     tempTE = TEStyleNew(&theRect,&theRect);
  326.  
  327.     CountDragItems(theDrag, &items);
  328.  
  329.     for (index = 1; index <= items; index++)
  330.         {
  331.         GetDragItemReferenceNumber(theDrag, index, &theItem);
  332.  
  333.         //    get the flags for a 'TEXT' flavor. If this returns noErr,
  334.         //    then we know that a 'TEXT' flavor exists in the item.
  335.  
  336.         result = GetFlavorDataSize(theDrag, theItem,'TEXT',&textSize);
  337.  
  338.         if (result == noErr) 
  339.             {
  340.             textData = NewPtr(textSize);
  341.             if (textData == 0L)
  342.                 {
  343.                 TEDispose(tempTE);
  344.                 return(memFullErr);
  345.                 }
  346.  
  347.             GetFlavorData(theDrag,theItem,'TEXT',textData,&textSize,0L);
  348.  
  349.             // check for optional styl data for the TEXT.
  350.  
  351.             stylHandle = 0L;
  352.             result = GetFlavorDataSize(theDrag,theItem,'styl',&stylSize);
  353.             if (result == noErr)
  354.                 {
  355.                 stylHandle = (StScrpHandle)NewHandle(stylSize);
  356.                 if (stylHandle == 0L)
  357.                     {
  358.                     TEDispose(tempTE);
  359.                     DisposePtr(textData);
  360.                     return(memFullErr);
  361.                     }
  362.  
  363.                 HLock((Handle)stylHandle);
  364.                 GetFlavorData(theDrag,theItem,'styl',*stylHandle,&stylSize,0L);
  365.                 HUnlock((Handle)stylHandle);
  366.                 }
  367.  
  368.             // insert this drag item's text into the tempTE
  369.  
  370.             TESetSelect(32767,32767,tempTE);
  371.             TEStyleInsert(textData,textSize,stylHandle,tempTE);
  372.  
  373.             DisposePtr(textData);
  374.             if (stylHandle)
  375.                 DisposeHandle((Handle) stylHandle);
  376.             }
  377.         }
  378.  
  379.     // pull the TEXT and styl data out of the tempTE handle.
  380.  
  381.     textData = NewPtr(textSize = (**tempTE).teLength);
  382.     if (textData == 0L)
  383.         {
  384.         TEDispose(tempTE);
  385.         return(memFullErr);
  386.         }
  387.     BlockMove(*(**tempTE).hText,textData,textSize);
  388.  
  389.     TESetSelect(0,32767,tempTE);
  390.     stylHandle = TEGetStyleScrapHandle(tempTE);
  391.  
  392.     TEDispose(tempTE);
  393.  
  394.     // if we actually received text, insert it into the destination
  395.  
  396.     if (textSize != 0)
  397.         {
  398.         // if the caret or highlighting is on the screen, remove it/them
  399.  
  400.         offset = caretOffset;
  401.  
  402.         if (caretOffset != -1)
  403.             {
  404.             DrawCaret(caretOffset,theDocument->theTE);
  405.             caretOffset = -1;
  406.             }
  407.  
  408.         if (attributes & dragHasLeftSenderWindow)
  409.             HideDragHilite(theDrag);
  410.  
  411.         //    If the drag occurred completely within the same window and the window is not
  412.         //    frontmost, bring the window forward and update its contents before completing
  413.         //    the drag.
  414.  
  415.         if ((attributes & dragInsideSenderWindow) && (theDocument->theWindow != FrontWindow()))
  416.             {
  417.             SelectWindow(theDocument->theWindow);
  418.             UpdateWindow(theDocument);
  419.             TEActivate(theDocument->theTE);
  420.             }
  421.  
  422.         // draw everything into offscreen pixmap.
  423.         if (theOffscreen = DrawOffscreen(theDocument->theWindow))
  424.             (**(theDocument->theTE)).inPort = (GrafPtr)theOffscreen->offscreenWorld;
  425.  
  426.         // if the window is not active, must activate TE before inserting
  427.         // text or the background hilite will not update correctly.
  428.  
  429.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  430.             if (!IsWindowHilited(theDocument->theWindow))
  431.                 TEActivate(theDocument->theTE);
  432.  
  433.         // if this window is also the sender, delete source selection if no option key.
  434.         if (moveText)
  435.             {
  436.             selStart = (**(theDocument->theTE)).selStart;
  437.             selEnd   = (**(theDocument->theTE)).selEnd;
  438.             if ( WhiteSpaceAtOffset(selStart - 1, theDocument->theTE) &&
  439.                 !WhiteSpaceAtOffset(selStart, theDocument->theTE) &&
  440.                 !WhiteSpaceAtOffset(selEnd - 1, theDocument->theTE) &&
  441.                  WhiteSpaceAtOffset(selEnd, theDocument->theTE))
  442.                 {
  443.                 if (GetCharAtOffset(selEnd, theDocument->theTE) == ' ')
  444.                     (**(theDocument->theTE)).selEnd++;
  445.                 }
  446.             if (insertPosition > selStart)
  447.                 {
  448.                 insertPosition -= ((**(theDocument->theTE)).selEnd -
  449.                                    (**(theDocument->theTE)).selStart);
  450.                 }
  451.             srcRect = (**theDocument->hiliteRgn).rgnBBox;
  452.             TEDelete(theDocument->theTE);
  453.             }
  454.  
  455.         InsertTextAtOffset(insertPosition,textData,textSize,stylHandle,theDocument->theTE);
  456.  
  457.         TEGetHiliteRgn(theDocument->hiliteRgn, theDocument->theTE);
  458.  
  459.         //    If the text is moving (not copying) within the same window, provide a ZoomRects
  460.         //    from the source to the destination before revealing the reflowed text.
  461.  
  462.         if (moveText)
  463.             {
  464.             theRect = (**theDocument->hiliteRgn).rgnBBox;
  465.             thePoint.h = thePoint.v = 0;
  466.             
  467.             SetPort(theWindow);
  468.  
  469.             LocalToGlobal(&thePoint);
  470.             OffsetRect(&srcRect,thePoint.h,thePoint.v);
  471.             OffsetRect(&theRect,thePoint.h,thePoint.v);
  472.             ZoomRects(&srcRect,&theRect,12,zoomDecelerate);
  473.             }
  474.         theDocument->dirty = true;
  475.         }
  476.  
  477.     DisposePtr(textData);
  478.  
  479.     if (stylHandle)
  480.         DisposeHandle((Handle)stylHandle);
  481.  
  482.     // undo the TEActivate, if needed
  483.     if (!IsWindowHilited(theDocument->theWindow))
  484.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  485.             TEDeactivate(theDocument->theTE);
  486.  
  487.     // show the offscreen bitmap
  488.     DrawOnscreen(theOffscreen);
  489.     (**(theDocument->theTE)).inPort = (GrafPtr)theDocument->theWindow;
  490.  
  491.     return noErr;
  492. }
  493.  
  494.  
  495. // *****************************************************************************
  496. // *
  497. // *    MyTrackingHandler()
  498. // *
  499. // *    This is the drag tracking handler for windows in the application.
  500. // *
  501. // *****************************************************************************
  502. pascal OSErr MyTrackingHandler(short message, WindowPtr /*theWindow*/, void* handlerRefCon, DragReference theDrag)
  503. {    
  504.     short                result, offset;
  505.     long                theTime = TickCount();
  506.     unsigned short        count, index;
  507.     unsigned long        flavorFlags, attributes;
  508.     ItemReference        theItem;
  509.     RgnHandle            theRgn;
  510.     Document*            theDocument = (Document*)handlerRefCon;
  511.     Document*            hitDoc;
  512.     Point                theMouse, localMouse;
  513.  
  514.     if ((message != dragTrackingEnterHandler) && (!canAcceptItems))
  515.         return(noErr);
  516.  
  517.     GetDragAttributes(theDrag, &attributes);
  518.  
  519.     switch (message)
  520.         {
  521.         case dragTrackingEnterHandler:
  522.  
  523.             //    We get called with this message the first time that a drag enters ANY
  524.             //    window in our application. Check to see if all of the drag items contain
  525.             //    TEXT. We only accept a drag if all of the items in the drag can be accepted.
  526.             
  527.             canAcceptItems = true;
  528.  
  529.             CountDragItems(theDrag, &count);
  530.  
  531.             for (index = 1; index <= count; index++)
  532.                 {
  533.                 GetDragItemReferenceNumber(theDrag,index,&theItem);
  534.  
  535.                 result = GetFlavorFlags(theDrag, theItem,'TEXT',&flavorFlags);
  536.  
  537.                 if (result != noErr)
  538.                     {
  539.                     canAcceptItems = false;
  540.                     break;
  541.                     }
  542.                 }
  543.             break;
  544.  
  545.         case dragTrackingEnterWindow:
  546.  
  547.             //    We receive an EnterWindow message each time a drag enters one of our
  548.             //    application's windows. We initialize our global variables for tracking
  549.             //    the drag through the window.
  550.  
  551.             caretTime = theTime;
  552.             caretOffset = lastOffset = -1;
  553.             caretShow = true;
  554.  
  555.             cursorInContent = false;
  556.  
  557.             break;
  558.  
  559.         case dragTrackingInWindow:
  560.  
  561.             //    We receive InWindow messages as long as the mouse is in one of our windows
  562.             //    during a drag. We draw the window highlighting and blink the insertion caret
  563.             //    when we get these messages.
  564.  
  565.             GetDragMouse(theDrag,&theMouse,0L);
  566.             localMouse = theMouse;
  567.             GlobalToLocal(&localMouse);
  568.  
  569.             //    Show or hide the window highlighting when the mouse enters or leaves the
  570.             //    TextEdit field in our window (we don't want to show the highlighting when
  571.             //    the mouse is over the window title bar or over the scroll bars).
  572.  
  573.             if (attributes & dragHasLeftSenderWindow) 
  574.                 {
  575.                 if (PtInRect(localMouse, &(**(theDocument->theTE)).viewRect))
  576.                     {
  577.                     if (!cursorInContent)
  578.                         {
  579.                         RectRgn(theRgn = NewRgn(),&(**(theDocument->theTE)).viewRect);
  580.                         ShowDragHilite(theDrag,theRgn,true);
  581.                         DisposeRgn(theRgn);
  582.                         }
  583.                     cursorInContent = true;
  584.                     }
  585.                 else
  586.                     {
  587.                     if (cursorInContent)
  588.                         HideDragHilite(theDrag);
  589.                     cursorInContent = false;
  590.                     }
  591.                 }
  592.  
  593.             offset = HitTest(theMouse, &hitDoc);
  594.  
  595.             //    If this application is the sender, do not allow tracking through
  596.             //    the selection in the window that sourced the drag.
  597.  
  598.             if (attributes & dragInsideSenderWindow)
  599.                 {
  600.                 if ((offset >= (**(theDocument->theTE)).selStart) &&
  601.                     (offset <= (**(theDocument->theTE)).selEnd))
  602.                         offset = -1;
  603.                 }
  604.  
  605.             if (hitDoc == theDocument)
  606.                 {
  607.                 insertPosition = offset;
  608.  
  609.                 //    Reset flashing counter if the offset has moved. This makes the
  610.                 //    caret blink only after the caret has stopped moving long enough.
  611.  
  612.                 if (offset != lastOffset)
  613.                     {
  614.                     caretTime = theTime;
  615.                     caretShow = true;
  616.                     }
  617.                 lastOffset = offset;
  618.  
  619.                 // flash caret
  620.  
  621.                 if (theTime - caretTime > gCaretTime)
  622.                     {
  623.                     caretShow = !caretShow;
  624.                     caretTime = theTime;
  625.                     }
  626.                 if (!caretShow)
  627.                     offset = -1;
  628.  
  629.                 // if caret offset has changed, move caret on screen
  630.  
  631.                 if (offset != caretOffset)
  632.                     {
  633.                     if (caretOffset != -1)
  634.                         {
  635.                         DrawCaret(caretOffset,theDocument->theTE);
  636.                         }
  637.                     if (offset != -1)
  638.                         {
  639.                         DrawCaret(offset,theDocument->theTE);
  640.                         }
  641.                     }
  642.  
  643.                 caretOffset = offset;
  644.                 }
  645.             else 
  646.                 {
  647.                 lastOffset = offset;
  648.                 insertPosition = -1;
  649.                 }
  650.             break;
  651.  
  652.         case dragTrackingLeaveWindow:
  653.  
  654.             // if the caret is on the screen, remove it.
  655.  
  656.             if (caretOffset != -1)
  657.                 {
  658.                 DrawCaret(caretOffset,theDocument->theTE);
  659.                 caretOffset = -1;
  660.                 }
  661.  
  662.             // remove window highlighting, if showing.
  663.  
  664.             if ((cursorInContent) && (attributes & dragHasLeftSenderWindow))
  665.                 HideDragHilite(theDrag);
  666.             break;
  667.  
  668.         case dragTrackingLeaveHandler:
  669.             break;
  670.  
  671.         }
  672.     return noErr;
  673. }
  674.  
  675.  
  676. // *****************************************************************************
  677. // *
  678. // *    DropLocationIsFinderTrash()
  679. // *
  680. // *    Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  681. // *
  682. // *****************************************************************************
  683. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation)
  684. {    
  685.     OSErr        result;
  686.     AEDesc        dropSpec;
  687.     FSSpec*        theSpec;
  688.     CInfoPBRec    thePB;
  689.     short        trashVRefNum;
  690.     long        trashDirID;
  691.  
  692.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  693.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  694.  
  695.     if ((dropLocation->descriptorType != typeNull) &&
  696.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  697.         {
  698.         HLock(dropSpec.dataHandle);
  699.         theSpec = (FSSpec*)*dropSpec.dataHandle;
  700.  
  701.         // get the directory ID of the given dropLocation object
  702.         thePB.dirInfo.ioCompletion = 0L;
  703.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  704.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  705.         thePB.dirInfo.ioFDirIndex = 0;
  706.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  707.  
  708.         result = PBGetCatInfoSync(&thePB);
  709.  
  710.         HUnlock(dropSpec.dataHandle);
  711.         AEDisposeDesc(&dropSpec);
  712.  
  713.         if (result != noErr)
  714.             return(false);
  715.  
  716.         // if the result is not a directory, it must not be the Trash.
  717.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  718.             return false;
  719.  
  720.         // get information about the Trash folder
  721.         FindFolder(theSpec->vRefNum,kTrashFolderType,kCreateFolder,&trashVRefNum,&trashDirID);
  722.  
  723.         //    if the directory ID of the dropLocation object is the same as the directory ID
  724.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  725.  
  726.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  727.             return true;
  728.         }
  729.     return false;
  730. }
  731.  
  732.  
  733. // *****************************************************************************
  734. // *
  735. // *    DragText()
  736. // *
  737. // *****************************************************************************
  738. short DragText(Document* theDocument, EventRecord* theEvent, RgnHandle hiliteRgn)
  739. {    
  740.     OSErr                theErr = noErr;
  741.     short                result;
  742.     RgnHandle            dragRegion, tempRgn;
  743.     Point                theLoc;
  744.     DragReference        theDrag;
  745.     StScrpHandle        theStyl;
  746.     AEDesc                dropLocation;
  747.     DragAttributes        attributes;
  748.     short                mouseDownModifiers, mouseUpModifiers, copyText;
  749.  
  750.     // copy the hilite region into dragRegion and offset it into global coordinates.
  751.     CopyRgn(hiliteRgn,dragRegion = NewRgn());
  752.     SetPt(&theLoc,0,0);
  753.     LocalToGlobal(&theLoc);
  754.     OffsetRgn(dragRegion,theLoc.h,theLoc.v);
  755.  
  756.     if (!WaitMouseMoved(theEvent->where))
  757.         return false;
  758.         
  759.     NewDrag(&theDrag);
  760.  
  761.     AddDragItemFlavor(theDrag,1,'TEXT',GetSelectedTextPtr(theDocument),GetSelectionSize(theDocument),0);
  762.  
  763.     theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  764.     HLock((Handle) theStyl);
  765.     AddDragItemFlavor(theDrag,1,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0);
  766.     HUnlock((Handle) theStyl);
  767.     DisposeHandle((Handle)theStyl);
  768.  
  769.     sendHandler = NewDragSendDataProc(&MySendDataProc);
  770.     theErr = SetDragSendProc(theDrag,sendHandler,(void*)theDocument);
  771.  
  772.     SetDragItemBounds(theDrag,1,&(**dragRegion).rgnBBox);
  773.  
  774.     // prepare the drag region
  775.     tempRgn = NewRgn();
  776.     CopyRgn(dragRegion,tempRgn);
  777.     InsetRgn(tempRgn,1,1);
  778.     DiffRgn(dragRegion,tempRgn,dragRegion);
  779.     DisposeRgn(tempRgn);
  780.  
  781.     /*
  782.     // use this if you want custom draw outlines..
  783.     if (gCanDrag)
  784.         {
  785. #if USESROUTINEDESCRIPTORS
  786.         sendHandler = NewDragSendDataProc(&MyDrawingProc);
  787.         theErr = SetDragDrawingProc(theDrag,drawHandler,0L);
  788. #else
  789.         theErr = SetDragDrawingProc(theDrag,MyDrawingProc,0L);
  790. #endif
  791.         }
  792.     */
  793.  
  794.     result = TrackDrag(theDrag,theEvent,dragRegion);
  795.  
  796.     if (result != noErr && result != userCanceledErr)
  797.         return true;
  798.  
  799.     // check to see if the drop occurred in the Finder's Trash. If the drop occurred
  800.     // in the Finder's Trash and a copy operation wasn't specified, delete the source selection
  801.  
  802.     GetDragAttributes(theDrag,&attributes);
  803.     if (!(attributes & dragInsideSenderApplication))
  804.         {
  805.         GetDropLocation(theDrag,&dropLocation);
  806.  
  807.         GetDragModifiers(theDrag,0L,&mouseDownModifiers,&mouseUpModifiers);
  808.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  809.  
  810.         if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation)))
  811.             {
  812.             TEDelete(theDocument->theTE);
  813.             theDocument->dirty = true;
  814.             }
  815.         AEDisposeDesc(&dropLocation);
  816.         }
  817.  
  818.     DisposeDrag(theDrag);
  819.     DisposeRgn(dragRegion);
  820.  
  821.     return true;
  822. }